package com.flaremars.picker.utils;


import com.flaremars.picker.bean.Coordinate;
import com.flaremars.picker.bean.CoordinateRange;

/**
 * Created by FlareMars on 2016/3/11
 */
public enum CoordinateUtils {
    INSTANCE;
    private static final Double EARTH_RADIUS = 6378.137;//地球半径
    private static final Double MIN_LAT = Math.toRadians(-90d);  // -PI/2
    private static final Double MAX_LAT = Math.toRadians(90d);   //  PI/2
    private static final Double MIN_LON = Math.toRadians(-180d); // -PI
    private static final Double MAX_LON = Math.toRadians(180d);  //  PI

    /**
     * 计算地球上两个坐标的距离
     * @param pos1 坐标1
     * @param pos2 坐标2
     * @return 距离，单位为米
     */
    public Double distance(Coordinate pos1, Coordinate pos2) {
        double radLat1 = rad(pos1.getLat());
        double radLat2 = rad(pos2.getLat());
        double a = radLat1 - radLat2;
        double b = rad(pos1.getLon()) - rad(pos2.getLon());

        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) +
                Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2)));
        s = s * EARTH_RADIUS * 1000;
        return s;
    }

    /**
     * 计算距离已知坐标 distance 千米 的[矩形]坐标范围
     * @param pos 已知坐标
     * @param distance 距离
     * @return 坐标范围
     */
    public CoordinateRange boundingCoordinates(Coordinate pos, Double distance) {

        double radLat = rad(pos.getLat());
        double radLon = rad(pos.getLon());
        double radDist = distance / EARTH_RADIUS;

        double minLat = radLat - radDist;
        double maxLat = radLat + radDist;

        double minLon, maxLon;
        if (minLat > MIN_LAT && maxLat < MAX_LAT) {
            double deltaLon = Math.asin(Math.sin(radDist) /
                    Math.cos(radLat));
            minLon = radLon - deltaLon;
            if (minLon < MIN_LON) minLon += 2d * Math.PI;
            maxLon = radLon + deltaLon;
            if (maxLon > MAX_LON) maxLon -= 2d * Math.PI;
        } else {
            // a pole is within the distance
            minLat = Math.max(minLat, MIN_LAT);
            maxLat = Math.min(maxLat, MAX_LAT);
            minLon = MIN_LON;
            maxLon = MAX_LON;
        }

        minLat = degree(minLat);
        maxLat = degree(maxLat);
        minLon = degree(minLon);
        maxLon = degree(maxLon);
        return new CoordinateRange(minLat,maxLat,minLon,maxLon);
    }

    private Double rad(Double d) {
        return d * Math.PI / 180.0;
    }

    private Double degree(Double rad) {
        return rad * 180.0 / Math.PI;
    }

}
